/*
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You under the Apache License, Version 2.0
 *  (the "License"); you may not use this file except in compliance with
 *  the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */
package com.xmlutils.utils;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import com.xmlutils.beans.ReportCaseBean;
import com.xmlutils.beans.ReportSuitBean;


/**   
* @Title: JsoupUtil.java 
* @Package com.utils.html 
* @Description: 
* @author 韦冬成    E-mail: 470029260@qq.com
* @date 2021年5月19日 上午10:35:53 
* @version V1.0  
* @user Tonsen
* @update 
* 2021年5月19日 上午10:35:53 
*/
public class JsoupUtil {
	private static final String TESTSUITE = "testsuite";
	private static final String TESTCASE = "testcase";
	private static final String CALSSNAME = "classname";
	private static final String NAME = "name";
	private static final String TIME = "time";
	private static final String FAILURE = "failure";
	private static final String FAILURES = "failures";
	private static final String MESSAGE = "message";
	private static final String TYPE = "type";
	private static final String TIMESTAMP = "timestamp";
	private static final String HOSTNAME = "hostname";
	private static final String TESTS = "tests";
	private static final String ERROR = "error";
	private static final String PASSED = "passed";
	private static final String IGNORED = "ignored";
	private static final String XMLNS = "xmlns";
	
	public static void main(String[] args) {
		try {
			xmlToHtmlReport("D:\\Projects\\g7ph\\reports\\Test Results - All_Tests2021051801 - 副本.html", 
					"D:\\Projects\\g7ph\\ITest\\g7ph_bluetooth.xml", 
					"D:\\Projects\\g7ph\\ITest\\jsoupDemo.html");
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("ok");
	}
	
	/**
	 * 读取xml报告到ReportSuitBean
	 * @param xmlFilePath junit的xml格式报告路径
	 * @return 返回整个报告的bean, 格式不正确或者没有内容返回null
	 * @throws IOException
	 */
	public static ReportSuitBean getReportSuitBeanFromXml(String xmlFilePath) throws IOException {
		ReportSuitBean suitBean = null;
		Document document = Jsoup.parse(new File(xmlFilePath),"UTF-8");
		Elements suites = document.getElementsByTag(TESTSUITE);
		if (suites.size() == 1) {
			Element suitEle = suites.get(0);
			suitBean = new ReportSuitBean(suitEle.attr(NAME), 
					suitEle.attr(TIMESTAMP), 
					suitEle.attr(HOSTNAME),
					suitEle.attr(TESTS),
					suitEle.attr(FAILURES),
					suitEle.attr(TIME));
			Elements casesEles = suitEle.getElementsByTag(TESTCASE);
			List<ReportCaseBean> caseList = Collections.synchronizedList(new ArrayList<ReportCaseBean>());
			for (Element element : casesEles) {
				ReportCaseBean caseBean = new ReportCaseBean();
				caseBean.setCaseClassname(element.attr(CALSSNAME));
				
				caseBean.setCaseName(element.attr(NAME));
				
				caseBean.setCaseTime(element.attr(TIME));
				Elements failueInfoEles = element.getElementsByTag(FAILURE);
				if (failueInfoEles.size() > 0) {
					caseBean.setCaseStatus(ERROR);
					caseBean.setCaseFailureMessage(failueInfoEles.attr(MESSAGE));
					caseBean.setCaseFailureType(failueInfoEles.attr(TYPE));
					caseBean.setCaseFailureCDATA(failueInfoEles.text());
				}
				caseList.add(caseBean);
			}
			suitBean.setCasesList(caseList);
		}
		return suitBean;
	}
	
	/**
	 * xml报告转html报告
	 * @param templatePath 模板文件路径，通过读取文件内容转换为模板
	 * @param xmlFilePath 输入xml文件路径
	 * @param exportHtmlFilePath html文件输出路径
	 * @throws IOException
	 */
	public static void xmlToHtmlReport(String templatePath, String xmlFilePath, String exportHtmlFilePath) throws IOException {
		ReportSuitBean reportSuitBean = getReportSuitBeanFromXml(xmlFilePath);
		
		File input = new File(templatePath);
		Document doc = Jsoup.parse(input, "UTF-8", "");
		/**
		 * 概览信息，头部信息
		 * */
		Element headerEle = doc.getElementById("header");
		// 总耗时
		headerEle.getElementsByClass("time").get(0).text(reportSuitBean.getSuitTime() + " s");
		// 概览
		Element strongEle = headerEle.getElementsByTag("h1").get(0).getElementsByTag("strong").get(0);
		strongEle.getElementsByTag("span").remove();
		strongEle.appendElement("span").addClass("total").attr(XMLNS, "http://www.w3.org/1999/xhtml").text(reportSuitBean.getSuitTests() + ", ");
		strongEle.appendElement("span").addClass("error").text(reportSuitBean.getSuitFailures() + " error, ");
		long passedCounter = 0;
		long testCounter = 0;
		try {
			testCounter = Long.parseLong(reportSuitBean.getSuitTests());
			passedCounter = testCounter - Long.parseLong(reportSuitBean.getSuitFailures());
		} catch (NumberFormatException e) {
		}
		strongEle.appendElement("span").addClass("passed").text(passedCounter + " passed, ");
		
		float passedRate = (float)passedCounter/(float)testCounter * 100;
		strongEle.appendElement("span").addClass("rate").text("passed rate: " + Math.round(passedRate) + "%");
		
		Element contentTreeEl = doc.getElementById("tree");
		contentTreeEl.getElementsByTag("li").remove();  // 删除模板里面的用例信息，增加真实的用例信息
		List<String> packagesList = Collections.synchronizedList(new ArrayList<String>());
		
		List<Element> htmlPackagesList = Collections.synchronizedList(new ArrayList<Element>());
		
		List<ReportCaseBean> caseBeans = reportSuitBean.getCasesList();
		for (ReportCaseBean reportCaseBean : caseBeans) {
			Element newEle;//待编辑的节点
			if (packagesList.contains(reportCaseBean.getCaseClassname())) {
				newEle = htmlPackagesList.get(packagesList.indexOf(reportCaseBean.getCaseClassname()));
			} else {
				packagesList.add(reportCaseBean.getCaseClassname());
				newEle = contentTreeEl.appendElement("li");//增加的类控件
				if (htmlPackagesList.size() > 0) {//默认是pass的，如果是faile的则改为failed的open
					newEle.addClass("level top open").attr("xmlns", "");
				} else {
					newEle.addClass("level top open");
				}
				htmlPackagesList.add(newEle);
				//包名,新发现的包，才增加包节点，否则在旧节点增加新case
				Element testPackageLiSpan001 = newEle.appendElement("span").text(reportCaseBean.getCaseClassname());
				Element testPackageLiSpanEm001 = testPackageLiSpan001.appendElement("em").addClass("time");
				testPackageLiSpanEm001.appendElement("div").addClass("time");//时间待修改
			}
			
			Element casesLevelFailedUlEl = newEle.appendElement("ul");
			if (reportCaseBean.getCaseStatus().equals(ERROR)) {//有faile 的case的包
				if (htmlPackagesList.size() > 0) {
					newEle.addClass("level top failed open").attr("xmlns", "");
				} else {
					newEle.addClass("level top failed open");
				}
				//failed 的 case
				Element caseFailLevelFailLiEl = casesLevelFailedUlEl.appendElement("li").addClass("level test failed open");
				Element caseFailLevelFailSpan = caseFailLevelFailLiEl.appendElement("span").text(reportCaseBean.getCaseName());
				Element caseFailLevelFailEm = caseFailLevelFailSpan.appendElement("em").addClass("time");//第一个em
				caseFailLevelFailEm.appendElement("div").addClass("time").text(reportCaseBean.getCaseTime() + " s");//第一个em中的一个div
				caseFailLevelFailSpan.appendElement("em").addClass("status").text(reportCaseBean.getCaseStatus());//第二个em
				// 失败信息
				Element caseFailLevelInfoUl = caseFailLevelFailLiEl.appendElement("ul");
				Element spanFailInfoSpan = caseFailLevelInfoUl.appendElement("li").addClass("text").appendElement("span").addClass("stderr");
				String[] failMsgInfos = reportCaseBean.getCaseFailureMessage().split(System.lineSeparator());
				for (String string : failMsgInfos) {
					spanFailInfoSpan.append(string);
					spanFailInfoSpan.append("<br />\r\n");
				}
				String[] failCADTAInfos = reportCaseBean.getCaseFailureCDATA().split(System.lineSeparator());
				for (String string : failCADTAInfos) {
					spanFailInfoSpan.append(string);
					spanFailInfoSpan.append("<br />\r\n");
				}
			} else {//passed的case
				//一下li是ul的子节点
				Element casesLevelLiEl = casesLevelFailedUlEl.appendElement("li").addClass("level test");
				Element casesLevelSpan = casesLevelLiEl.appendElement("span").text(reportCaseBean.getCaseName());
				Element casesLevelEm = casesLevelSpan.appendElement("em").addClass("time");	//第一个em
				casesLevelEm.appendElement("div").addClass("time").text(reportCaseBean.getCaseTime() + " s");			//第一个em中的一个div
				casesLevelSpan.appendElement("em").addClass("status").text(PASSED);      	//第二个em
			}
		}
		// 底部时间信息
		Element footerEle = doc.getElementById("footer");
		footerEle.getElementsByTag("p").remove();
		footerEle.appendElement("p").text("Generated by xunitreport on " + MyUtils.getNowTimeSpace());
		
		
		FileOutputStream writerStreamTxt = new FileOutputStream(new File(exportHtmlFilePath));
		BufferedWriter bufferedWriterTxt = new BufferedWriter(new OutputStreamWriter(writerStreamTxt, "UTF-8"));
		bufferedWriterTxt.write(doc.toString());
		bufferedWriterTxt.close();
		writerStreamTxt.close();
	}
	
	/**
	 * xml报告转html报告
	 * @param htmlTemplateStr html模板字符串内容
	 * @param xmlFilePath 输入xml文件路径
	 * @param exportHtmlFilePath html文件输出路径
	 * @throws IOException
	 */
	public static void xmlToHtmlReportUseTemplateString(String htmlTemplateStr, String xmlFilePath, String exportHtmlFilePath) throws IOException {
		ReportSuitBean reportSuitBean = getReportSuitBeanFromXml(xmlFilePath);
		
		Document doc = Jsoup.parse(htmlTemplateStr);
		/**
		 * 概览信息，头部信息
		 * */
		Element headerEle = doc.getElementById("header");
		// 总耗时
		headerEle.getElementsByClass("time").get(0).text(reportSuitBean.getSuitTime() + " s");
		// 概览
		Element strongEle = headerEle.getElementsByTag("h1").get(0).getElementsByTag("strong").get(0);
		strongEle.getElementsByTag("span").remove();
		strongEle.appendElement("span").addClass("total").attr(XMLNS, "http://www.w3.org/1999/xhtml").text(reportSuitBean.getSuitTests() + ", ");
		strongEle.appendElement("span").addClass("error").text(reportSuitBean.getSuitFailures() + " error, ");
		long passedCounter = 0;
		long testCounter = 0;
		try {
			testCounter = Long.parseLong(reportSuitBean.getSuitTests());
			passedCounter = testCounter - Long.parseLong(reportSuitBean.getSuitFailures());
		} catch (NumberFormatException e) {
		}
		strongEle.appendElement("span").addClass("passed").text(passedCounter + " passed, ");
		
		float passedRate = (float)passedCounter/(float)testCounter * 100;
		strongEle.appendElement("span").addClass("rate").text("passed rate: " + Math.round(passedRate) + "%");
		
		Element contentTreeEl = doc.getElementById("tree");
		contentTreeEl.getElementsByTag("li").remove();  // 删除模板里面的用例信息，增加真实的用例信息
		List<String> packagesList = Collections.synchronizedList(new ArrayList<String>());
		
		List<Element> htmlPackagesList = Collections.synchronizedList(new ArrayList<Element>());
		
		List<ReportCaseBean> caseBeans = reportSuitBean.getCasesList();
		for (ReportCaseBean reportCaseBean : caseBeans) {
			Element newEle;//待编辑的节点
			if (packagesList.contains(reportCaseBean.getCaseClassname())) {
				newEle = htmlPackagesList.get(packagesList.indexOf(reportCaseBean.getCaseClassname()));
			} else {
				packagesList.add(reportCaseBean.getCaseClassname());
				newEle = contentTreeEl.appendElement("li");//增加的类控件
				if (htmlPackagesList.size() > 0) {//默认是pass的，如果是faile的则改为failed的open
					newEle.addClass("level top open").attr("xmlns", "");
				} else {
					newEle.addClass("level top open");
				}
				htmlPackagesList.add(newEle);
				//包名,新发现的包，才增加包节点，否则在旧节点增加新case
				Element testPackageLiSpan001 = newEle.appendElement("span").text(reportCaseBean.getCaseClassname());
				Element testPackageLiSpanEm001 = testPackageLiSpan001.appendElement("em").addClass("time");
				testPackageLiSpanEm001.appendElement("div").addClass("time");//时间待修改
			}
			
			Element casesLevelFailedUlEl = newEle.appendElement("ul");
			if (reportCaseBean.getCaseStatus().equals(ERROR)) {//有faile 的case的包
				if (htmlPackagesList.size() > 0) {
					newEle.addClass("level top failed open").attr("xmlns", "");
				} else {
					newEle.addClass("level top failed open");
				}
				//failed 的 case
				Element caseFailLevelFailLiEl = casesLevelFailedUlEl.appendElement("li").addClass("level test failed open");
				Element caseFailLevelFailSpan = caseFailLevelFailLiEl.appendElement("span").text(reportCaseBean.getCaseName());
				Element caseFailLevelFailEm = caseFailLevelFailSpan.appendElement("em").addClass("time");//第一个em
				caseFailLevelFailEm.appendElement("div").addClass("time").text(reportCaseBean.getCaseTime() + " s");//第一个em中的一个div
				caseFailLevelFailSpan.appendElement("em").addClass("status").text(reportCaseBean.getCaseStatus());//第二个em
				// 失败信息
				Element caseFailLevelInfoUl = caseFailLevelFailLiEl.appendElement("ul");
				Element spanFailInfoSpan = caseFailLevelInfoUl.appendElement("li").addClass("text").appendElement("span").addClass("stderr");
				String[] failMsgInfos = reportCaseBean.getCaseFailureMessage().split(System.lineSeparator());
				for (String string : failMsgInfos) {
					spanFailInfoSpan.append(string);
					spanFailInfoSpan.append("<br />\r\n");
				}
				String[] failCADTAInfos = reportCaseBean.getCaseFailureCDATA().split(System.lineSeparator());
				for (String string : failCADTAInfos) {
					spanFailInfoSpan.append(string);
					spanFailInfoSpan.append("<br />\r\n");
				}
			} else {//passed的case
				//一下li是ul的子节点
				Element casesLevelLiEl = casesLevelFailedUlEl.appendElement("li").addClass("level test");
				Element casesLevelSpan = casesLevelLiEl.appendElement("span").text(reportCaseBean.getCaseName());
				Element casesLevelEm = casesLevelSpan.appendElement("em").addClass("time");	//第一个em
				casesLevelEm.appendElement("div").addClass("time").text(reportCaseBean.getCaseTime() + " s");			//第一个em中的一个div
				casesLevelSpan.appendElement("em").addClass("status").text(PASSED);      	//第二个em
			}
		}
		// 底部时间信息
		Element footerEle = doc.getElementById("footer");
		footerEle.getElementsByTag("p").remove();
		footerEle.appendElement("p").text("Generated by xunitreport on " + MyUtils.getNowTimeSpace());
		
		
		FileOutputStream writerStreamTxt = new FileOutputStream(new File(exportHtmlFilePath));
		BufferedWriter bufferedWriterTxt = new BufferedWriter(new OutputStreamWriter(writerStreamTxt, "UTF-8"));
		bufferedWriterTxt.write(doc.toString());
		bufferedWriterTxt.close();
		writerStreamTxt.close();
	}
}
